﻿#define REALPLATFORM

using System;
using System.ComponentModel;
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace JDKD.BMCS.Instruction
{
        public static class SerialPortCommunication
        {
                #region 常量

                private const byte P = 0X0D;

                #endregion 常量

                #region 字段

                /// <summary>
                /// 获取当前工作串口
                /// </summary>
                /// <value>当前工作串口</value>
                private static SerialPort serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);

                private static Thread SerialPortWorkThread = new Thread(SerialPortWorkThreadFunction);

                /// <summary>
                /// 发送队列
                /// </summary>
                private static Queue<Instruction> SendInstructionQueues = new Queue<Instruction>();

                private static bool isWaitingReceive = false;

                private static byte[] receiveInstructionBuffer = new byte[13];

                private static int receiveInstructionIndex = 0;
                        
                #endregion 字段
                        
                #region 属性

                private static String portName = null;

                /// <summary>
                /// 获取或设置当前串口名称
                /// </summary>
                /// <value>
                /// 当前串口名称
                /// </value>
                public static string PortName
                {
                        get
                        {
                                return portName;
                        }
                        set
                        {
                                if (portName != value)
                                {
                                        if (serialPort != null)
                                        {
                                                serialPort.Close();
                                        }

                                        portName = value;
                                        serialPort.PortName = portName;

                                        serialPort.Open();

                                }
                        }
                }


                public static bool IsOpen { get; private set; }
                

                #endregion 属性

                #region 事件

                public static event EventHandler<PushStateEventArgs> PushStateUpdated;

                #endregion 事件

                #region 方法

                public static void Initialize(string serialPortName = "COM1")
                {
                        PortName = serialPortName;

                        IsOpen = true;

                        serialPort.DataReceived += SerialPortDataReceived;

                        SerialPortWorkThread.Start();
                }

                private static void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
                {
                        if (serialPort.BytesToRead < 1)
                        {
                                return;
                        }

                        byte[] buffer = new byte[serialPort.BytesToRead];
                        serialPort.Read(buffer, 0, buffer.Length);

                        

                        for (int i = 0; i < buffer.Length; i++)
                        {
                                switch (receiveInstructionIndex)
                                {
                                        case 0:
                                                if (buffer[i] == 0xFF)
                                                {
                                                        receiveInstructionIndex++;
                                                }
                                                break;

                                        case 1:
                                                if (buffer[i] == 0x5F)
                                                {
                                                        
                                                        receiveInstructionBuffer[receiveInstructionIndex - 1] = buffer[i];
                                                        receiveInstructionIndex++;
                                                }
                                                else
                                                {
                                                        receiveInstructionIndex = 0;
                                                }
                                                break;

                                        case 13:
                                                receiveInstructionBuffer[receiveInstructionIndex - 1] = buffer[i];

                                                bool isLeftMotion = (receiveInstructionBuffer[10] & 0xF0) > 0 ;
                                                bool isRightMotion = (receiveInstructionBuffer[10] & 0x0F) > 0;

                                                OnPushStateUpdated(new PushStateEventArgs(isLeftMotion, isRightMotion));

                                                receiveInstructionIndex = 0;

                                                break;

                                        default:
                                                receiveInstructionBuffer[receiveInstructionIndex - 1] = buffer[i];
                                                receiveInstructionIndex++;
                                                break;
                                }
                        }
                }

                public static void UnInitialize()
                {
                        SerialPortWorkThread.Abort();
                        serialPort.Close();
                }

                /// <summary>
                /// 串口通讯模式下摄像机光圈调整指令
                /// </summary>
                /// <param name="cameraId">摄像机编号</param>
                /// <param name="modify">为true光圈增大，为false光圈减小</param>
                /// <returns>字节指令数组</returns>
                public static void SendApertureInstruction(int cameraId, bool modify)
                {
                        SendInstructionQueues.Enqueue(new ApertureInstruction((Byte)cameraId, modify));
                }

                /// <summary>
                /// 串口通讯模式下摄像机焦距调整指令
                /// </summary>
                /// <param name="cameraId">摄像机编号</param>
                /// <param name="modify">为true焦距增大，为false焦距减小</param>
                /// <returns>字节指令数组</returns>
                public static void SendFocalDistanceInstruction(int cameraId, bool modify)
                {
                        SendInstructionQueues.Enqueue(new FocalDistanceInstruction((byte)cameraId, modify));
                }
                
                /// <summary>
                /// 获取串口通讯模式下摄像机聚焦调整指令
                /// </summary>
                /// <param name="cameraId">摄像机编号</param>
                /// <param name="modify">为true聚焦增大，为false聚焦减小</param>
                /// <returns>字节指令数组</returns>
                public static void SendFocusInstruction(int cameraId, bool modify)
                {
                        SendInstructionQueues.Enqueue(new FocusInstruction((Byte)cameraId, modify));
                }

                /// <summary>
                /// 串口通讯模式下摄像机光圈、聚焦、焦距调整幅度指令
                /// </summary>
                /// <param name="span">每次光圈聚焦、焦距、聚焦调整通电毫秒数</param>
                /// <returns>字节指令数组</returns>
                public static void SendSpanInstruction(int span)
                {
                        SendInstructionQueues.Enqueue(new SpanInstruction((Byte)span));
                }

                /// <summary>
                /// 发送挡板动作指令
                /// </summary>
                /// <param name="channelId">流编号</param>
                /// <param name="modify">挡板是否上升</param>
                public static void SendRaiseInstruction(int channelId, bool modify)
                {
                        SendInstructionQueues.Enqueue(new RaiseInstruction((Byte)channelId, modify));
                }

                /// <summary>
                /// 发送翻钢机动作指令
                /// </summary>
                /// <param name="channelId">流编号</param>
                /// <param name="modify">是否翻钢</param>
                public static void SendShiftInstruction(int channelId, bool modify)
                {
                        
                        SendInstructionQueues.Enqueue(new ShiftInstruction((Byte)channelId, modify));
                }

                /// <summary>
                /// 发送推钢机动作指令
                /// </summary>
                /// <param name="groupId">摄像机组编号</param>
                /// <param name="modify">是否推刚</param>
                public static void SendPushInstruction(int groupId, bool modify)
                {
                        SendInstructionQueues.Enqueue(new PushInstruction((Byte)groupId, modify));
                }

                ///<summary>
                /// 发送推刚机状态询问指令
                /// </summary>
                public static void SendPushQueryInstruction()
                {
                        SendInstructionQueues.Enqueue(new QueryPushInstruction());
                }

                public static void SerialPortWorkThreadFunction()
                {
                        for (; ; )
                        {
                                Instruction i;
                                if (SendInstructionQueues.Count > 0)
                                {
                                        i = SendInstructionQueues.Dequeue();
                                }
                                else
                                {
                                        i = new QueryPushInstruction();

                                        //Trace.WriteLine("Send Query Push Instruction.");
                                }

                                serialPort.Write(i.Content, 0, i.Content.Length);
                                isWaitingReceive = i.IsWaitingReceive;

                                Thread.Sleep(50);
                        }
                }

                public static void OnPushStateUpdated(PushStateEventArgs e)
                {
                        if (PushStateUpdated != null)
                        {
                                PushStateUpdated(null, e);
                        }
                }

                #endregion 方法
        }
}